import {observe} from './observer/index.js'
import {proxy, isObject} from './util/index.js'
import Watcher from './observer/watcher.js';

export function initState(vm) {
  const opts = vm.$options;
  // 
  if(opts.props) {
    initProps(vm)
  }
  if(opts.methods) {
    initMethod(vm)
  }
  if(opts.data) {
    initData(vm)
  }
  if(opts.computed) {
    initComputed(vm, opts.computed)
  }
  if(opts.watch) {
    initWatch(vm, opts.watch)
  }
}

function initProps() {}
function initMethod() { }
// 数据初始化工作
function initData(vm) {
  // console.log(vm)
  let data = vm.$options.data
  // 
  data = vm._data = typeof data === 'function' ? data.call(vm) : data
  // 为了让用户更好取值 代理一层 
  for(let key in data) {
    proxy(vm, '_data', key)
  }

  // 对象劫持
  observe(data)
}
// 内部原理也是通过 watcher 来实现的
function initComputed(vm, computeds) {
  // 存放着所有计算属性对应的watcher
  const watchers = vm._computedWatchers = {}
  for(let key in computeds) {
    const userDef = computeds[key]
    const getter = typeof userDef === 'function' ? userDef : userDef.get;
    // lazy true 表示计算属性  一开始不去调用 getter
    watchers[key] = new Watcher(vm, getter, ()=>{}, {lazy: true})

    // 计算属性可以直接通过vm来取值
    defineComputed(vm, key, userDef);
  }
}



// 属性描述器
const sharedPropertyDefinition = {
  enumerable: true,
  configurable: true,
  get: () => {},
}
function createComputedGetter(key) {
  return function() {
    let watcher = this._computedWatchers[key]
    // 是true的话  就去调用用户的方法  通过这个来添加缓存
    if(watcher.dirty) {
      watcher.evaluate()
    }
    return watcher.value
  }
}

function defineComputed(target, key, userDef) {
  let obj = {...sharedPropertyDefinition}
  if(typeof userDef === 'function') {
    obj.get = createComputedGetter(key)
  }else{
    obj.get = createComputedGetter(key);
    obj.set = userDef.set || (()=>{})
  }
  Object.defineProperty(target, key, obj)
}

function initWatch(vm, watch) {
  for(let key in watch) {
    const handler = watch[key]
    if(Array.isArray(handler)) {
      for(let i = 0; i<handler.length; i++) {
        createWatcher(vm, key, handler[i])
      }
    }else{
      createWatcher(vm, key, handler)
    }
  }
}

function createWatcher(vm, key, handler, options={}) {
  if(isObject(handler)) {
    options = handler
    handler = handler.handler
  }
  if(typeof handler === 'string') {
    handler = vm.$options.methods[handler]
  }
  return vm.$watch(key, handler, options)
}

export function stateMixin(Vue) {
  Vue.prototype.$watch = function(exprOrFn, cb, options) {
    const vm = this
    // 用户watcher   之前那个是渲染watcher
    options.user = true  // 代表是用户watcher
    new Watcher(vm, exprOrFn, cb, options)
  }
}